#include "wrap.h"

// 这是一个使用select端口轮训的TCP服务器

#define SERV_PORT 8888
#define MAXSIZE 4096

void select_server(){
	// conn_fd和serv_fd分别为客户端和服务器的文件描述符
	// ret，i,j,n分别用来记录
	int ret, i, j, conn_fd, serv_fd, n;
	// serv_addr和clit_addr分别为服务器和客户端的地址结构
	struct sockaddr_in serv_addr, clit_addr;
	char buf[MAXSIZE];
	socklen_t clit_addr_len;

	// 该数组用来保存客户端的数量
	int client[1024];
	
	// fd集合，rset表示读文件描述符集，allset为临时集合
	fd_set rset, allset;
	// maxfd用来标记最大的文件描述符
	int maxfd, maxi = -1;

	// 还是TCP服务器通信的正常流程
	serv_fd = Socket(AF_INET, SOCK_STREAM, 0);

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	// 设置服务器端口复用
	int opt = 1;
	setsockopt(serv_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
	
	Bind(serv_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

	// 进行Listen监听
	Listen(serv_fd, 128);

	// 使用select函数进行I/O监听
	FD_ZERO(&allset);			// 首先使用FD_ZERO清空数据集
	FD_SET(serv_fd, &allset);	// 将serv_fd添加到allset集合中
	
	maxfd = serv_fd;			// 首先将maxfd指向服务器监听描述符
	memset(client, -1, sizeof(client));

	printf("The server into bolcking...\n");

	while(1){
		rset = allset;

		// 将select设为阻塞,
		ret = select(maxfd+1, &rset, NULL, NULL, NULL);

		if(ret < 0){
			sys_err("select error");
		}

		// 如果ret大于0，说明有新的客户端连接请求
		if(FD_ISSET(serv_fd, &rset)){
			// 如果ret不为零，说明select中有客户端连接
			clit_addr_len = sizeof(clit_addr);
			conn_fd = Accept(serv_fd, (struct sockaddr*)&clit_addr, &clit_addr_len);
			
			printf("%d client accept!\n", conn_fd);

			// 找client中没有使用的位置,并将accept返回的文件描述符存放到client中
			for(i = 0; i<FD_SETSIZE; ++i){
				if(client[i] < 0){
					client[i] = conn_fd;
					break;
				}
			}
			
			// 判断客户端是否到达上限
			if(i == FD_SETSIZE){
					fputs("too many clients\n", stderr);
					exit(1);
			}

			// 向监控文件描述符集合allset中添加新的文件描述符connfd
			FD_SET(conn_fd, &allset);
			
			if(maxfd < conn_fd){
				maxfd = conn_fd;
			}

			// 确保maxi存放的总是client[]的最后一个元素下标
			if(i > maxi){
				maxi = i;
			}

			if(ret == 1){
				continue;	
			}
		}

		// 检测那个clients的数据就绪
		for(i = 0; i<= maxi; ++i){
			int	sockfd = client[i];
			if(sockfd < 0){
				continue;
			}

			if(FD_ISSET(sockfd, &rset)){
				n = Read(sockfd, buf, MAXSIZE);
				if(n == 0){
					printf("%d exit!\n", client[i]);
			
					close(sockfd);

					// 解除select对应文件描述符的集合
					FD_CLR(sockfd, &allset);
					client[i] = -1;
				}else if(n > 0){
					for(j=0; j<n; ++j){
						buf[j] = toupper(buf[j]);
					}
					Write(sockfd, buf, n);
				}
			}
		}
	}
	close(serv_fd);

}

int main(int argc, char *argv[]){
	select_server();
}

